`timescale 1ns/1ps
`default_nettype none

module coe_writer (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_sdram_clk, // 150M
    input  wire         I_rst_n,
    // ram 缓存
    input  wire [ 9:0]  I_ram_addr,
    input  wire [31:0]  I_ram_data_w,
    output wire [31:0]  O_ram_data_r,
    input  wire         I_ram_wren,

    //request
    input  wire         I_req,
    input  wire         I_rw,             //写请求为1，读请求为0
    output wire         O_busy,           //忙状态
    input  wire [23:0]  I_sdram_addr,     //请求sdram的地址
    input  wire [10:0]  I_rw_len,        //读写长度 最大1024*8bit 低2位必定为0

    // sdram mux
    output wire         O_mux_req,
    input  wire         I_mux_ack,
    input  wire         I_mux_irq, // 中断，表示有更高优先级的模块正在请求SDRAM控制权
    output wire         O_mux_cs_n,
    output wire         O_mux_ras_n,
    output wire         O_mux_cas_n,
    output wire         O_mux_we_n,
    output wire [1:0]   O_mux_ba,
    output wire [10:0]  O_mux_addr,
    output wire [31:0]  O_mux_dq_out,
    input  wire [31:0]  I_mux_dq_in,
    output wire         O_mux_dq_oe,
    output wire [3:0]   O_mux_dqm
);
//------------------------Parameter----------------------
`include "../led_display/sdram_common.vh"

localparam
    RL = 3 + SD_MR_CL;  // read data latency

// wait count
localparam
     WAIT_TIME = (1 << SD_MR_BL) + DELAY_RP - 1  -1; //确保突发4完成后进入等待时间
    
// fsm
localparam [8:0]
    IDLE  = 1,
    PREP  = 1<<1,
    ACT   = 1<<2,
    NOP0  = 1<<3,
    NOP1  = 1<<4,
    READ_WRITE = 1<<5,
    WAIT  = 1<<6,
    WAIT1 = 1<<7,
    LOOP  = 1<<8;
    
localparam 
    IDLE_ID  = 0,
    PREP_ID  = 1,
    ACT_ID   = 2,
    NOP0_ID  = 3,
    NOP1_ID  = 4,
    READ_WRITE_ID = 5,
    WAIT_ID  = 6,
    WAIT1_ID = 7,
    LOOP_ID  = 8;

//------------------------Local signal-------------------
// read request (@I_sclk)
reg         busy;
reg  [1:0]  req_sr;
reg  [3:0]  done_sr;

// fsm (@I_sdram_clk)
reg  [8:0]  state;
reg  [8:0]  next;
reg  [3:0]  start_sr;
reg  [1:0]  over_sr;
reg  [3:0]  delay_cnt;
reg         first_act; // 第一次ACT

// sdram mux
reg         mux_req;
reg  [3:0]  mux_cmd;
reg  [1:0]  mux_ba;
reg  [10:0] mux_addr;
// reg  [31:0] mux_dq_out;
reg         mux_dq_oe;
reg  [3:0]  mux_dqm;
reg  [8:0]  data_cnt;

reg  [20:0] sdram_addr;

// ram
reg         ram_wren;
reg  [7:0]  ram_addr;
reg  [31:0] ram_data;
wire [31:0] ram_q;

// write ram
reg  [RL:0] read_sr;

reg  [2:0]  data_rw_len;
reg  [3:0]  rw_addr_en;
reg  [3:0]  rw_len_en;
reg  [2:0]  data_cnt_t;

//------------------------Instantiation------------------

sdpram_256x32bit ram(
    .clock_a(I_sclk),
    .address_a( I_ram_addr[9:2]),
    .data_a(I_ram_data_w),
    .wren_a(I_ram_wren),
    .q_a(O_ram_data_r),
    
    .clock_b(I_sdram_clk),
    .address_b( ram_addr),
    .data_b( ram_data ),
    .wren_b(ram_wren),
    .q_b(ram_q)
    );
// tdpram_lpm #( 
    // .A_ADDRESS_WIDTH    (   8  ),
    // .A_DATA_WIDTH       (   32   ),
    // .A_BYTEENA_WIDTH    (   1    ),
    // .B_ADDRESS_WIDTH    (   8   ),
    // .B_DATA_WIDTH       (   32  )
// )ram (/*{{{*/
    // .clka           (I_sclk),
    // .wea            (I_ram_wren),
    // .rea            (1'b1),
    // .addra          ( I_ram_addr[9:2]),
    // .dina           (I_ram_data_w),
    // .douta          (O_ram_data_r),
    // .byteena_a      (1'b1),

    // .clkb           (I_sdram_clk),
    // .web            (ram_wren),
    // .reb            (1'b1),
    // .addrb          ( ram_addr),
    // .dinb           ( ram_data ),
    // .doutb          (ram_q)
// );

assign O_busy = busy;

// busy
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        busy <= 1'b0;
    else if (!busy && I_req)
        busy <= 1'b1;
    else if (done_sr[3:2] == 2'b01)
        busy <= 1'b0;
end

// req_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        req_sr <= 'b0;
    else if (!busy && I_req)
        req_sr <= 2'b11;
    else
        req_sr <= req_sr << 1;
end

// done_sr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        done_sr <= 1'b0;
    else
        done_sr <= {done_sr[2:0], over_sr[1]};
end

// start_sr
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        start_sr <= 1'b0;
    else
        start_sr <= {start_sr[2:0], req_sr[1]};
end

// over_sr
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        over_sr <= 'b0;
    else if (state == LOOP && next == IDLE)
        over_sr <= 2'b11;
    else
        over_sr <= over_sr << 1;
end

// state
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            if (start_sr[3])
                next = PREP;
            else
                next = IDLE;
        end

        PREP: begin
            next = ACT;
        end

        ACT: begin
            if (!first_act)
                next = NOP0;
            else if (I_mux_ack)
                next = NOP1;
            else
                next = ACT;
        end

        NOP0:begin
            next = NOP1;
        end
        
        NOP1:begin
            next = READ_WRITE;
        end

        READ_WRITE:begin
            if (data_cnt == 1'b0)
                next = WAIT;
            else if (I_mux_irq)
                next = WAIT;
            else if(sdram_addr[7:2] == 6'h3f)
                next = WAIT;
            else
                next = ACT;
        end

        WAIT: begin
            if (delay_cnt >= WAIT_TIME)
                next = WAIT1;
            else
                next = WAIT;
        end
        
        WAIT1: begin
            next = LOOP;
        end

        LOOP: begin
            if (data_cnt == 1'b0)
                next = IDLE;
            else
                next = ACT;
        end
        
        default: begin
            next = IDLE;
        end
    endcase
end

// first_act
always @(posedge I_sdram_clk) begin
    if (state[IDLE_ID] || state[LOOP_ID])
        first_act <= 1'b1;
    else if (state[READ_WRITE_ID])begin
        first_act <= 1'b0;
    end
end

always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        mux_dq_oe <= 1'b0;
    else if (state[PREP_ID])
        mux_dq_oe <= I_rw;
end

// delay_cnt
always @(posedge I_sdram_clk ) begin
    if (state[READ_WRITE_ID])
        delay_cnt <= 'd0;
    else
        delay_cnt <= delay_cnt + 1'b1;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++sdram mux++++++++++++++++++++++
assign O_mux_req    = mux_req;
assign O_mux_cs_n   = mux_cmd[3];
assign O_mux_ras_n  = mux_cmd[2];
assign O_mux_cas_n  = mux_cmd[1];
assign O_mux_we_n   = mux_cmd[0];
assign O_mux_ba     = mux_ba;
assign O_mux_addr   = mux_addr;
// assign O_mux_dq_out = mux_dq_out;
assign O_mux_dq_out = ram_q;
assign O_mux_dq_oe  = mux_dq_oe;
// assign O_mux_dqm    = 1'b0;
assign O_mux_dqm    = mux_dqm;

// mux_req
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        mux_req <= 1'b0;
    else if (state[ACT_ID])
        mux_req <= 1'b1;
    else if (state[LOOP_ID])
        mux_req <= 1'b0;
end

// mux_cmd
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        mux_cmd <= SD_CMD_NOP;
    else if (state[ACT_ID]) begin
        if (!first_act || I_mux_ack)
            mux_cmd <= SD_CMD_NOP;
        else
            mux_cmd <= SD_CMD_ACT;
    end
    else if (state[READ_WRITE_ID])
        if(mux_dq_oe)
            mux_cmd <= SD_CMD_WRIT;
        else 
            mux_cmd <= SD_CMD_READ;
    else
        mux_cmd <= SD_CMD_NOP;
end

// mux_ba
always @(posedge I_sdram_clk) begin
    if (state[IDLE_ID])
        sdram_addr[20:0] <= I_sdram_addr[22:2];
    else if( state[READ_WRITE_ID])begin
        sdram_addr[1:0] <= 'b0 ;
        sdram_addr[20:2] <= sdram_addr[20:2] + 1'b1;
    end
end

// mux_ba
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        mux_ba <= 'b0;
    else if ( state[ACT_ID] || state[READ_WRITE_ID] ) 
        mux_ba <= sdram_addr[18:17];
end

// mux_addr
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        mux_addr <= 'b0;
    else if (state[ACT_ID])
        mux_addr <= {sdram_addr[20:19] ,sdram_addr[16:8]};
    else if (state[READ_WRITE_ID] )
        mux_addr <= {1'b1, 2'b00 , sdram_addr[7:0]};
end

always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n) 
        data_rw_len <= 'd4;
    else if ( state[ACT_ID] )
        data_rw_len <= 3'd4 - sdram_addr[1:0] ;
end

always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)begin
        data_cnt <= 'b0;
    end
    else if (state[PREP_ID]) begin
         data_cnt <= I_rw_len[10:2];
    end
    else if ( state[NOP1_ID] ) begin
        if(data_cnt_t >= data_rw_len)
            data_cnt <= data_cnt - data_rw_len ;
        else 
            data_cnt <= 'd0 ;
    end 
end



always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        data_cnt_t <= 'b0;
    else if(state[ACT_ID]) begin
        if(data_cnt[8:2] != 0)
            data_cnt_t <= 3'b100;
        else 
            data_cnt_t <= {1'b0, data_cnt[1:0]};
    end
end


always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        rw_len_en <= 4'b0000;
    else if ( state[NOP1_ID] ) 
        case(data_cnt_t[2:0])
        0: rw_len_en[3:0] <= 4'b0000;
        1: rw_len_en[3:0] <= 4'b1000;
        2: rw_len_en[3:0] <= 4'b1100;
        3: rw_len_en[3:0] <= 4'b1110;
        default : rw_len_en[3:0] <= 4'b1111;
        endcase
    else
        rw_len_en[3:0] <= {rw_len_en[2:0] , 1'b0};
end


always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        rw_addr_en <= 4'b0000;
    else if ( state[NOP1_ID] ) 
        case(sdram_addr[1:0])
            0:rw_addr_en[3:0] <= 4'b1111;
            1:rw_addr_en[3:0] <= 4'b1110;
            2:rw_addr_en[3:0] <= 4'b1100;
            3:rw_addr_en[3:0] <= 4'b1000;
        endcase
    else 
        rw_addr_en[3:0] <= {rw_addr_en[2:0] , 1'b0};
end

//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        mux_dqm <= 'b0;
    else if (mux_dq_oe)
        mux_dqm <= { 4{~(rw_addr_en[3] & rw_len_en[3] )} } ;
    else
        mux_dqm <= 'b0;
end

//{{{+++++++++++++++++++++write ram++++++++++++++++++++++
// read_sr
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        read_sr <= 'b0;
    else
        read_sr[RL:0] <= {read_sr[RL-1:0], (rw_addr_en[3] & rw_len_en[3])};
end

// ram_wren
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        ram_wren <= 1'b0;
    else if (read_sr[RL] && mux_dq_oe =='b0)
        ram_wren <= 1'b1;
    else
        ram_wren <= 1'b0;
end

// ram_waddr
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        ram_addr <= 'b0;
    else if (state[PREP_ID])
        ram_addr <= 'b0;
    else if (ram_wren )
        ram_addr <= ram_addr + 1'b1;
    else if(mux_dq_oe && rw_addr_en[3] && rw_len_en[3])
        ram_addr <= ram_addr + 1'b1;
end

// ram_data
always @(posedge I_sdram_clk or negedge I_rst_n) begin
    if (!I_rst_n)
        ram_data <= 'b0;
    else if (read_sr[RL]) begin
        ram_data <= I_mux_dq_in ;
    end
end

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
